package org.lib.redishello.controller;

import java.time.Duration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.annotation.Resource;

import org.lib.redishello.dto.AddToSetDTO;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;

/**
 * HTTP操作Redis
 * @author ben
 * @date 2021-08-23 13:39:07 CST
 */
@RestController
@RequestMapping(value="/redis")
@Slf4j
public class RedisController {

	@Resource
	private RedisTemplate<String, Object> redisTemplate;
	
	// -------------value-------------
	
	/**
	 * 前缀: /value
	 */
	private final static String PATH_VALUE = "/value/";
	
	/**
	 * 设置String值
	 * @author ben
	 * @date 2021-08-23 14:14:33 CST
	 * @param key 非空
	 * @param value 非空
	 * @param timeout 必须大于0，过期秒数
	 * @return 成功返回true
	 */
	@PostMapping(value=PATH_VALUE + "/setValueStr")
	public boolean setValue(@RequestParam String key, @RequestParam String value, @RequestParam Long timeout) {
		if (!(StringUtils.hasText(key) && StringUtils.hasText(value) && timeout != null && timeout > 0)) {
			throw new RuntimeException("参数错误");
		}
		
		log.info("key={}, value={}, timeout={}", key, value, timeout);
		
		// 注意，第三个参数是 Duration
		redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(timeout));
		return redisTemplate.hasKey(key);
	}
	
	/**
	 * 获取String值
	 * @author ben
	 * @date 2021-08-23 14:15:12 CST
	 * @param key
	 * @return 值
	 */
	@GetMapping(value=PATH_VALUE + "/getValueStr")
	public String getValueStr(@RequestParam String key) {
		if (!StringUtils.hasText(key)) {
			throw new RuntimeException("参数错误");
		}

		log.info("key={}, ttl={} seconds", key, redisTemplate.getExpire(key));
		
		return (String) redisTemplate.opsForValue().get(key);
	}

	/**
	 * 设置长整型
	 * @author ben
	 * @date 2021-08-23 14:15:29 CST
	 * @param key 非空
	 * @param value 非null
	 * @param timeout 必须大于0
	 * @return
	 */
	@PostMapping(value=PATH_VALUE + "/setValueLong")
	public boolean setValue(@RequestParam String key, @RequestParam Long value, @RequestParam Long timeout) {
		if (!(StringUtils.hasText(key) && value != null && timeout != null && timeout > 0)) {
			throw new RuntimeException("参数错误");
		}
		
		redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(timeout));
		return redisTemplate.hasKey(key);
	}
	
	/**
	 * 获取长整型值
	 * @author ben
	 * @date 2021-08-23 14:15:56 CST
	 * @param key
	 * @return
	 */
	@GetMapping(value=PATH_VALUE + "/getValueLong")
	public Long getValueLong(@RequestParam String key) {
		if (!StringUtils.hasText(key)) {
			throw new RuntimeException("参数错误");
		}
		
		log.debug("key={}, ttl={} seconds", key, redisTemplate.getExpire(key));
		
		Long retval = null;
		Object val = redisTemplate.opsForValue().get(key);
		if (String.class.equals(val.getClass())) {
			retval = Long.valueOf(String.valueOf(val));
		} else {
			log.warn("Redis中没有key={}", key);
		}
		
		return retval;
	}
	
	/**
	 * 给长整型增加值
	 * @author ben
	 * @date 2021-08-23 14:16:09 CST
	 * @param key
	 * @param delta 非null，可正，可负
	 * @return
	 */
	@PostMapping(value=PATH_VALUE + "/incrementValueLong")
	public Long incrementValueLong(@RequestParam String key, @RequestParam Long delta) {
		if (!(StringUtils.hasText(key) && delta != null )) {
			throw new RuntimeException("参数错误");
		}
		
		log.info("key={}, delta={}", key, delta);
		
		// 发生异常：io.lettuce.core.RedisCommandExecutionException: 
		// ERR value is not an integer or out of range
		// 改造 redisTemplate 的序列化器 见 {@link RedisConfig}
		return redisTemplate.opsForValue().increment(key, delta);
	}
	
	// -------------set-------------

	/**
	 * 前缀：set/
	 */
	private final static String PATH_SET = "/set/";
	
	/**
	 * 添加到集合
	 * @author ben
	 * @date 2021-08-23 15:55:10 CST
	 * @param dto
	 * @return
	 */
	@PostMapping(value=PATH_SET + "/add")
	public Long addToSet(@RequestBody AddToSetDTO dto) {
		String key = dto.getKey();
		String[] values = dto.getValues();
		if (!StringUtils.hasText(key) || Objects.isNull(values)) {
			throw new RuntimeException("参数错误");
		}
		
		if (values.length == 0) {
			return 0L;
		}
		
		return redisTemplate.opsForSet().add(key, values);
	}
	
	/**
	 * 获取集合全部元素
	 * @author ben
	 * @date 2021-08-23 15:55:24 CST
	 * @param key
	 * @return
	 */
	@GetMapping(value=PATH_SET + "/getAll")
	public Set<String> getAllSet(@RequestParam String key) {
		Set<String> retset = new HashSet<>(32);
		
		// 第一种方式
		// 返回数据不符合预期
//		Set<Object> set = redisTemplate.opsForSet().members(key);
//		Optional.of(set).ifPresent(item -> {
//			// 仅执行一次
//			retset.add(String.valueOf(item));
//		});

		// 第二种获取元素的方式 符合预期！
		List<Object> list = redisTemplate.opsForSet().randomMembers(key, redisTemplate.opsForSet().size(key));
		list.forEach(obj->{
			retset.add((String) obj);
		});
		
		return retset;
	}
	
	/**
	 * 删除并返回集合中一个随机元素
	 * @author ben
	 * @date 2021-08-23 15:55:31 CST
	 * @param key
	 * @return
	 */
	@PostMapping(value=PATH_SET + "/popOne")
	public String popSet(@RequestParam String key) {
		return (String) redisTemplate.opsForSet().pop(key);
	}
	
}
